3 pillars of functional programming